package controllers

import config.{Global, MyAwait, MyFile, MyJson, MyRow}
import dao._
import models.Web
import org.apache.commons.io.FileUtils
import play.api.data.Form
import play.api.data.Forms._
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, ControllerComponents}
import tools.Circos
import utils.{ExecCommand, TableUtils}

import java.nio.file.Files
import javax.inject.Inject
import scala.concurrent.ExecutionContext

class BlockController @Inject()(genomeDao: GenomeDao,
                                blockDao: BlockDao,
                                blockRangeDao: BlockRangeDao,
                                cc: ControllerComponents)
                               (implicit exec: ExecutionContext) extends AbstractController(cc) with MyRow with MyAwait with MyFile with MyJson {

  def syntenicPage = Action { implicit request =>
    Ok(views.html.mcscanx.syntenicPage())
  }

  def syntenyInfoPage(id: Int) = Action.async { implicit request =>
    blockRangeDao.getById(id).map { x =>
      Ok(views.html.mcscanx.syntenyInfoPage(x))
    }
  }

  def getComparison(species: String, chr: String) = Action.async { implicit request =>
    blockRangeDao.getBySpeciesAndChr(species, chr).map { x =>
      val json = x.distinct.map { db =>
        val name = TableUtils.genomeName(db)
        db -> name
      }
      Ok(Json.toJson(json))
    }
  }

  def validBlockIdExist(blockId: String) = Action.async { implicit request =>
    blockRangeDao.exsitBlockId(blockId.trim).map { x =>
      Ok(Json.obj("valid" -> x.nonEmpty.toString, "id" -> x.map(_.id)))
    }
  }

  case class SyntenicData(species: String, chr: String, db: Seq[String])

  val SyntenicForm = Form(
    mapping(
      "species" -> text,
      "chr" -> text,
      "db" -> seq(text)
    )(SyntenicData.apply)(SyntenicData.unapply)
  )

  def getComparisonResult = Action.async { implicit request =>
    val data = SyntenicForm.bindFromRequest.get
    blockRangeDao.getComparison(data.species, data.chr, data.db).map { x =>
      val json = data.db.map { ref =>
        val query = s"${Global.path}/blockLen/$ref.txt".readLines.map(_.split("\t").head)
        val xx = x.filter(y => query.contains(y.query) || query.contains(y.ref))

        Json.obj("title" -> TableUtils.genomeName(ref), "ref" -> ref.split('.').mkString("_"), "result" -> Circos.circosPlot(data.species, data.chr, ref, xx.filter(y => y.querySpecies == ref || y.refSpecies == ref)))
      }
      Ok(Json.toJson(json))
    }
  }


  def getChr(species: String) = Action { implicit request =>
    Ok(Json.toJson(s"${Global.path}/blockLen/$species.txt".readLines.map(_.split("\t").head)))
  }


  def getBlockRangeData(species1: String, species2: String) = Action.async { implicit request =>
    blockRangeDao.getBySpecies(species1, species2).map { x =>
      Ok(Json.toJson(x.map(_.getMapByT())))
    }
  }

  def getBlockInfo(id: Int) = Action.async { implicit request =>
    blockRangeDao.getById(id).map { x =>
      val json = Json.obj("name" -> x.name,
        "orA" -> TableUtils.genomeName(x.querySpecies), "loA" -> s"${x.query}:${x.queryStart}-${x.queryEnd}", "idA" -> x.querySpecies,
        "orB" -> TableUtils.genomeName(x.refSpecies), "loB" -> s"${x.ref}:${x.refStart}-${x.refEnd}", "idB" -> x.refSpecies
      )

      val (queryStart, queryEnd) = if (x.queryStart < x.queryEnd) {
        (x.queryStart, x.queryEnd)
      } else {
        (x.queryEnd, x.queryStart)
      }

      val genes = genomeDao.getByRange(x.querySpecies, x.query, queryStart, queryEnd).toAwait
      val id = genes.map(_.id)
      val geneMap = genes.map(z => z.id -> z).toMap

      val blocks = blockDao.getByIds(id).toAwait.map(y => y.id -> y.block.split(";") /*.filter { b =>
        b.split('.').length == geneStart(x.refSpecies)
      }*/).toMap
      val blockGene = genomeDao.getByIds(blocks.map(_._2.map(q => TableUtils.geneIdToID.getOrElse(q, 0)).filter(_ != 0)).flatten.toSeq.distinct).toAwait.map(b => b.geneid -> b).toMap

      val sv = blocks.map { b =>
        b._2.map(z => geneMap(b._1) -> blockGene(z))
      }.flatten.toSeq

      val data = sv.filter(_._2.species == x.refSpecies).sortBy(_._1.start).map { case (s1, s2) =>
        Json.obj("idA" -> s1.id, "geneA" -> s1.geneid, "locusA" -> s"${s1.chr}:${s1.start}-${s1.end};${s1.strand}",
          "idB" -> s2.id, "geneB" -> s2.geneid, "locusB" -> s"${s2.chr}:${s2.start}-${s2.end};${s2.strand}")
      }
      val (refStart, refEnd) = if (x.refStart < x.refEnd) {
        (x.refStart, x.refEnd)
      } else {
        (x.refEnd, x.refStart)
      }
      val selectB = sv.filter(_._2.chr == x.ref).filter(_._2.start >= refStart).filter(_._2.end <= refEnd)
      val s1Map = selectB.map(_._1).distinct.zipWithIndex.toMap
      val s2Map = selectB.map(_._2).distinct.zipWithIndex.toMap

      val relate = selectB.map { s =>
        Array(s1Map(s._1) + 1, s2Map(s._2) + 1)
      }
      val selectA = selectB.map(_._1)
      val otherA = sv.map(_._1).diff(selectA)
      val rectA = (selectA ++ otherA).map { y => Seq(y.geneid, y.start.toString, y.end.toString) }.distinct
      val rectB = selectB.map(_._2).map { y => Seq(y.geneid, y.start.toString, y.end.toString) }.distinct
      val row = Json.obj("A" -> Json.obj("name" -> x.query, "min" -> x.queryStart, "max" -> x.queryEnd, "rect" -> rectA),
        "B" -> Json.obj("name" -> x.ref, "min" -> x.refStart, "max" -> x.refEnd, "rect" -> rectB))
      Ok(Json.obj("data" -> row, "relate" -> relate, "table" -> data, "block" -> json))
    }
  }


  case class SpeciesData(species1: String, species2: String)

  val SpeciesForm = Form(
    mapping(
      "species1" -> text,
      "species2" -> text
    )(SpeciesData.apply)(SpeciesData.unapply)
  )

  def getDotPlotBySpecies = Action { implicit request =>
    val form = SpeciesForm.bindFromRequest.get
    val s1 = form.species1
    val s2 = form.species2

    val files = s"${Global.path}/config/synteny".listFile.filter(x => x.getName.contains(s1) && x.getName.contains(s2))

    val name = files.head.getName.split('.').init.mkString(".")

    val na = name.split("_vs_")
    val (xAxis, yAxis, xT, yT) = if (na.head == s1) {
      (s"${Global.path}/blockLen/$s1.txt".readLines.map(_.split("\t").head),
        s"${Global.path}/blockLen/$s2.txt".readLines.map(_.split("\t").head),
        Web.genomeName(s1), Web.genomeName(s2)
      )
    } else {
      (s"${Global.path}/blockLen/$s2.txt".readLines.map(_.split("\t").head),
        s"${Global.path}/blockLen/$s1.txt".readLines.map(_.split("\t").head),
        Web.genomeName(s2), Web.genomeName(s1)

      )
    }


    val json = files.find(_.getName.endsWith("json")).get.readFileToString
    val pos = files.find(_.getName.endsWith("pos")).get.readFileToString

    Ok(Json.obj("series" -> json, "xAxis" -> xAxis, "yAxis" -> yAxis, "pos" -> pos, "xT" -> xT, "yT" -> yT))
  }

  def getComparisonAllResult = Action.async { implicit request =>
    val form = SpeciesForm.bindFromRequest.get
    val s1 = form.species1
    val s2 = form.species2
    blockRangeDao.getBySpecies(s1, s2).map { x =>
      val query = s"${Global.path}/blockLen/$s1.txt".readLines.map(_.split("\t").head)
      val ref = s"${Global.path}/blockLen/$s2.txt".readLines.map(_.split("\t").head)
      val xx = x.filter(y => query.contains(y.query) || query.contains(y.ref)).filter(y => ref.contains(y.query) || ref.contains(y.ref))
      val json = Json.obj("result" -> Circos.circosPlotAll(s1, s2, xx))
      Ok(Json.toJson(json))
    }
  }

  case class DownloadData(types: String, svg: String,name:String)

  val DownloadForm = Form(
    mapping(
      "types" -> text,
      "svg" -> text,
      "name" -> text
    )(DownloadData.apply)(DownloadData.unapply)
  )

  def downloadSvg = Action { implicit request =>
    val data = DownloadForm.bindFromRequest.get
    val tmpDir = Files.createTempDirectory("tmpDir").toString
    val svg = s"$tmpDir/synteny.svg"
    FileUtils.writeStringToFile(svg.toFile, data.svg)
    println(tmpDir)

    val exec2 = new ExecCommand
    val out = s"$tmpDir/synteny.${data.types}"
    val convert = s"convert $svg $out"
    exec2.exect(Array(convert), tmpDir)

    Ok(Json.toJson(tmpDir))
  }


  def download(path:String,name:String,types:String) = Action{implicit request=>
    val paths = path.replaceAll("\\\\","/")
    println(paths)
    if(paths.startsWith("/tmp/tmpDir")) {
      val p = s"$path/synteny.$types".replaceAll("\\\\", "/")
      Ok.sendFile(p.toFile, onClose = () => {
        paths.delete
      }).withHeaders(
        //缓存
        CACHE_CONTROL -> "max-age=3600",
        CONTENT_DISPOSITION -> s"attachment; filename=${name}.${types}",
        CONTENT_TYPE -> "application/x-download"
      )
    }else{
      Ok(Json.toJson("false"))
    }
  }
}
